package cn.gjsm.utils;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.theokanning.openai.service.OpenAiService;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

@Slf4j
public class SessionUtil {
    //缓存容器的初始容量
    final static int initialCapacity = 512;
    //并发等级（根据cpu情况设置  推荐内核数*2）    
    final static int concurrencyLevel = 4;
    //最大缓存条数。子类在构造方法中调用setMaximunSize(int size)来更改
    final static int maximumSize = 1000;
    //当缓存项在指定的时间段内没有被读或写就会被回收
    final static int expireAfterAcess = 60 * 2;
    //时间单位（分钟）
    final static TimeUnit timeUnit = TimeUnit.MINUTES;

    /**
     *cookie: 2 hours
     */
    public static final int EXPIRY_DAYS = 2 * 24 * 60 * 60;
    public static final String USER_KEY = "user_key";

    // 自动加载缓存、避免出现脏读
    static volatile LoadingCache<String, OpenAiService> loadingCache = null;

    static {
        loadingCache = CacheBuilder.newBuilder()
           .initialCapacity(initialCapacity)
           .maximumSize(maximumSize)
           .concurrencyLevel(concurrencyLevel)
           .expireAfterAccess(expireAfterAcess, timeUnit)
           .build(new CacheLoader<>() {
               @Override
               public OpenAiService load(String k) throws Exception {
                   log.info("CacheLoader, K: {}", k);
                   return OpenAiClient.getOpenAiService();
               }
           });
    }

    public static OpenAiService getAiService(HttpServletRequest request, HttpServletResponse response) {
        Cookie[] cookies = request.getCookies();
        if (Objects.isNull(cookies)) {
            cookies = new Cookie[0];
        }
        var hasOp = Arrays.stream(cookies).filter(t -> USER_KEY.equals(t.getName())).findFirst();

        if (hasOp.isEmpty()) {
            var userKey = UUID.randomUUID().toString();
            Cookie userKeyCookie = new Cookie(USER_KEY, userKey);
            userKeyCookie.setMaxAge(EXPIRY_DAYS);
            userKeyCookie.setPath(request.getContextPath());
            response.addCookie(userKeyCookie);

            log.info("SettingCache: {}, {}", userKey, request.getRequestURL() + request.getQueryString());
            OpenAiService openAiService = OpenAiClient.getOpenAiService();
            loadingCache.put(userKey, openAiService);

            return  openAiService;
        }else {
            var userKeyCookie = hasOp.get();
            var userKey = userKeyCookie.getValue();
            log.info("CacheHit: {}", userKey);
            try {
                return loadingCache.get(userKey);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void cleanCache(){
        loadingCache.cleanUp();
    }
}
